<!DOCTYPE html>
<html>
<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="utf-8">
  

  
  <title>蔡进东的日志</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta name="description" content="不要在最该奋斗的年纪去偷懒!">
<meta property="og:type" content="website">
<meta property="og:title" content="蔡进东的日志">
<meta property="og:url" content="http://cai4633.github.io/page/2/index.html">
<meta property="og:site_name" content="蔡进东的日志">
<meta property="og:description" content="不要在最该奋斗的年纪去偷懒!">
<meta property="og:locale" content="zh-CN">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="蔡进东的日志">
<meta name="twitter:description" content="不要在最该奋斗的年纪去偷懒!">
  
  
    <link rel="icon" href="/favicon.png">
  
  
    <link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
  
  <link rel="stylesheet" href="/css/style.css">
</head>
</html>
<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="banner"></div>
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a href="/" id="logo">蔡进东的日志</a>
      </h1>
      
      <h2 id="subtitle-wrap">
        <a href="/" id="subtitle">不要在最该奋斗的年纪选择去偷懒，只有度过一段连自己都被感动了的日子，才能变成那个最好的自己.</a>
      </h2>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        <a id="main-nav-toggle" class="nav-icon"></a>
        
        <a class="main-nav-link" href="/">Home</a>
        
        <a class="main-nav-link" href="/archives">Archives</a>
        
      </nav>
      <nav id="sub-nav">
        
        <a id="nav-search-btn" class="nav-icon" title="搜索"></a>
      </nav>
      <div class="local-search local-search-plugin">
        <input type="search" placeholder="站内搜索" id="local-search-input" class="local-search-input-cls" />
        <div id="local-search-result" class="local-search-result-cls"></div>
      </div>
      <!-- <div id="search-form-wrap">
        <form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit">&#xF002;</button><input type="hidden" name="sitesearch" value="http://cai4633.github.io"></form>
      </div> -->
    </div>
  </div>
</header>

      <div class="outer">
        <section id="main">
  
    <article id="post-在线记账项目遇到的问题" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/09/24/在线记账项目遇到的问题/" class="article-date">
  <time datetime="2020-09-24T06:35:21.000Z" itemprop="datePublished">2020-09-24</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/前端/">前端</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/09/24/在线记账项目遇到的问题/">在线记账项目遇到的问题</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <ol>
<li>-webkit-overflow-scroll: touch/auto;可以使移动端浏览器产生过滚动回弹效果，但是下拉body背景会影响fixed布局元素。</li>
<li>移动端少用100vh,因为100vh包括菜单栏高度和工具栏会导致页面不可控，可以用 css -100% 或者 js document.innerHeight来替代。</li>
<li>flex 布局最后一行左对齐： 给父元素添加after伪元素，设置flex:1</li>
</ol>

      
    </div>
    <footer class="article-footer">
      <!--<a data-url="http://cai4633.github.io/2020/09/24/在线记账项目遇到的问题/" data-id="ckjqth4sf002n2gw08akhckt5" class="article-share-link">Share</a>-->
      
      
    </footer>
  </div>
  <!---->
</article>


  
    <article id="post-虚拟DOM和DOM diff" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/09/18/虚拟DOM和DOM diff/" class="article-date">
  <time datetime="2020-09-17T16:02:24.000Z" itemprop="datePublished">2020-09-18</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/前端/">前端</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/09/18/虚拟DOM和DOM diff/">虚拟 DOM 和 DOM diff</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="虚拟-DOM"><a href="#虚拟-DOM" class="headerlink" title="虚拟 DOM"></a>虚拟 DOM</h2><h3 id="虚拟-DOM-是什么"><a href="#虚拟-DOM-是什么" class="headerlink" title="虚拟 DOM 是什么"></a>虚拟 DOM 是什么</h3><p>虚拟 dom 的本质就是一个能代表 dom 树的<strong>JS 对象。</strong> 它是相对于浏览器所渲染出来的真实 dom 而言的。它包括标签类型、子元素、属性以及事件等。</p>
<p>在虚拟 DOM 技术出现之前，我们要改变页面内容只能通过遍历查询 dom 树的方式找到需要修改的 dom 然后修改 dom 属性或者结构等，来达到更新 ui 的目的。因为每次查询 dom 几乎都需要遍历整颗 dom 树。所以这种方式比较消耗性能。虚拟 dom 对象（ js 对象）以对象嵌套的方式来表示 dom 树，查找 js 对象的属性变化要比查询 dom 树的性能开销小。</p>
<h3 id="虚拟-DOM-实例"><a href="#虚拟-DOM-实例" class="headerlink" title="虚拟 DOM 实例"></a>虚拟 DOM 实例</h3><p>虚拟 dom 在不同的框架中，有着不一样的具体形式。<br><strong>React 虚拟 DOM</strong> :</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">const vNode = &#123;</span><br><span class="line">  key: null,</span><br><span class="line">  type:&apos;div&apos;,</span><br><span class="line">  ref: null,</span><br><span class="line">  props: &#123;</span><br><span class="line">    children:[</span><br><span class="line">      &#123;type:&apos;span&apos; ...&#125;,</span><br><span class="line">      &#123;type:&apos;span&apos; ...&#125;,</span><br><span class="line">    ],</span><br><span class="line">    onClick:()=&gt;&#123;&#125;,</span><br><span class="line">    className: &apos;main&apos;</span><br><span class="line">  &#125;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>Vue 虚拟 DOM</strong> :</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">const vNode = &#123;</span><br><span class="line">  tag:&apos;div&apos;,</span><br><span class="line">  data: &#123;</span><br><span class="line">    on:&#123;</span><br><span class="line">      click:()=&gt;&#123;&#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    class: &apos;main&apos;</span><br><span class="line">  &#125;,</span><br><span class="line">  children:[</span><br><span class="line">    &#123;tag:&apos;span&apos; ...&#125;,</span><br><span class="line">    &#123;tag:&apos;span&apos; ...&#125;,</span><br><span class="line">  ],</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="创建虚拟-DOM"><a href="#创建虚拟-DOM" class="headerlink" title="创建虚拟 DOM"></a>创建虚拟 DOM</h3><p>React 内置函数：<code>React.createElement(type,[props],[...children])</code><br>简化写法： jsx + babel<br>Vue 内置函数：<code>h(tag,data,children)</code><br>简化写法： vue template + vue-loader</p>
<h3 id="虚拟-DOM-的优点"><a href="#虚拟-DOM-的优点" class="headerlink" title="虚拟 DOM 的优点"></a>虚拟 DOM 的优点</h3><ol>
<li>减少 dom 操作次数<br>真实 dom 插入 1000 个节点需要进行 1000 次 dom 操作，虚拟 dom 可以将多次操作合并为一次 dom 操作。</li>
<li>减少 dom 操作范围<br>通过 DOM diff 算法对比新旧虚拟 dom 树,去掉不必要的 dom 操作。例如：添加 1000 个节点，虚拟 dom 通过对比发现有 900 个相同的节点已经存在于相同的层级位置，那么虚拟 dom 只会添加 100 个节点。</li>
<li>跨平台<br>由于虚拟 dom 本质是 JS 对象，所以它可以变成 DOM、小程序、IOS 应用或者安卓应用。</li>
</ol>
<h3 id="虚拟-DOM-的缺点"><a href="#虚拟-DOM-的缺点" class="headerlink" title="虚拟 DOM 的缺点"></a>虚拟 DOM 的缺点</h3><ol>
<li>需要额外的创建函数</li>
<li>需要额外的转义构建工具</li>
<li>节点数量少时虚拟 dom 效率高，但是节点数量多时虚拟 dom 性能比不上原生dom（vue接近原生dom，react性能偏差）。</li>
</ol>
<h2 id="DOM-diff"><a href="#DOM-diff" class="headerlink" title="DOM diff"></a>DOM diff</h2><h3 id="DOM-diff-是什么"><a href="#DOM-diff-是什么" class="headerlink" title="DOM diff 是什么"></a>DOM diff 是什么</h3><p>它的本质是一个函数，类似于<code>patches = patch(newVDom, oldVDom)</code>。其中patches就是需要进行的 dom 操作。diff 算法在执行时有三个维度，分别是<strong>tree diff、component diff 和 element diff，</strong>执行时按层级顺序依次执行，它们的差异仅仅因为 diff 粒度不同、执行先后顺序不同。</p>
<h3 id="DOM-diff-的优点"><a href="#DOM-diff-的优点" class="headerlink" title="DOM diff 的优点"></a>DOM diff 的优点</h3><p>DOM diff算法会对比 oldNode 与 newNode 的区别，从而<strong>减少不必要的渲染</strong>。</p>
<h3 id="DOM-diff-的问题"><a href="#DOM-diff-的问题" class="headerlink" title="DOM diff 的问题"></a>DOM diff 的问题</h3><p>DOM diff在同层级对比中有bug。造成页面渲染错误。<strong>同一层级的一组节点</strong>可以通过唯一的id进行区分, 所以可以给节点设定唯一的key。从而消除bug。<strong>key只能是number和string类型，一定不要用index作为key值。</strong></p>
<h3 id="几个使用key的场景"><a href="#几个使用key的场景" class="headerlink" title="几个使用key的场景"></a>几个使用key的场景</h3><ol>
<li>v-for (vue)</li>
<li>同一层级有相同标签节点的<strong>transition动画</strong></li>
</ol>

      
    </div>
    <footer class="article-footer">
      <!--<a data-url="http://cai4633.github.io/2020/09/18/虚拟DOM和DOM diff/" data-id="ckjqth4si003g2gw048rfh94z" class="article-share-link">Share</a>-->
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/DOM-diff/">DOM diff</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/vDOM/">vDOM</a></li></ul>

    </footer>
  </div>
  <!---->
</article>


  
    <article id="post-一篇文章搞懂promise" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/08/18/一篇文章搞懂promise/" class="article-date">
  <time datetime="2020-08-18T12:24:25.000Z" itemprop="datePublished">2020-08-18</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/js/">js</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/08/18/一篇文章搞懂promise/">一篇文章搞懂promise</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="Promise-解决了什么问题？"><a href="#Promise-解决了什么问题？" class="headerlink" title="Promise 解决了什么问题？"></a>Promise 解决了什么问题？</h2><p>在传统的异步编程中，如果异步之间存在依赖关系，就需要通过层层嵌套回调的方式满足这种依赖，如果嵌套层数过多，可读性和可以维护性都会变得很差，产生所谓的<strong>“回调地狱”</strong>。而 <code>Promise</code> 将嵌套调用改为<strong>链式调用</strong>，增加了可阅读性和可维护性。也就是说，Promise 用一种更加<strong>友好的代码组织方式</strong>解决了<strong>异步嵌套</strong>问题。Promise的缺陷是无法中断请求。<br>产生回调地狱的原因主要有两点：</p>
<ol>
<li>(异步请求)嵌套调用，第一个函数的输出往往是第二个函数的输入；(Promise thenable 链式调用可以解决)</li>
<li>处理多个异步请求并发，开发时需要同步请求最终的结果。（Promise.all()可以解决）</li>
</ol>
<h2 id="then-的链式调用和值穿透"><a href="#then-的链式调用和值穿透" class="headerlink" title="then 的链式调用和值穿透"></a>then 的链式调用和值穿透</h2><p>在我们使用 Promise 的时候，当 then 函数中 return 了一个值，不管是什么值，我们都能在下一个 then 中获取到，这就是所谓的 then 的链式调用。而且，当我们不在 then 中放入参数，例：promise.then().then()，那么其后面的 then 依旧可以得到之前 then 返回的值，这就是所谓的值的穿透。</p>
<h2 id="Promise-的-API"><a href="#Promise-的-API" class="headerlink" title="Promise 的 API"></a>Promise 的 API</h2><ul>
<li>Promise.resolve() 产生一个成功的 promise, 直接将值变成成功结果</li>
<li>Promise.reject()  产生一个失败的 promise，直接将值变成错误结果</li>
<li>Promise.all()   解决并发问题的，多个异步并发获取最终的结果（如果有一个失败则失败)</li>
<li>Promise.race()  处理多个请求,谁先完成用谁的</li>
<li>Promise.prototype.catch()   捕获 promise 的异常</li>
<li>Promise.prototype.finally()  无论如何都会执行</li>
</ul>
<h2 id="Promise-A-规范"><a href="#Promise-A-规范" class="headerlink" title="Promise/A+ 规范"></a>Promise/A+ 规范</h2><ol>
<li>promise 有三个状态：<strong>pending，fulfilled，rejected</strong>, 默认状态是 pending</li>
<li>new promise 时， 需要传递一个 executor(resolve, reject)执行器，<strong>执行器立即执行</strong></li>
<li>promise 有一个<strong>value 保存成功状态</strong>的值，可以是 undefined/thenable/promise</li>
<li>promise 有一个<strong>reason 保存失败状态</strong>的值</li>
<li>promise <strong>只能</strong>从 pending 到 rejected, 或者从 pending 到 fulfilled，状态一旦确认，就不会再改变</li>
<li>promise 必须有一个 then 方法，then 接收两个参数，分别是 promise 成功的回调 onFulfilled, 和 promise 失败的回调 onRejected</li>
<li>如果调用 then 时，promise 已经成功，则执行 onFulfilled，参数是 promise 的 value；</li>
<li>如果调用 then 时，promise 已经失败，那么执行 onRejected, 参数是 promise 的 reason；</li>
<li>如果 then 中抛出了异常，那么就会把这个异常作为参数，传递给下一个 then 的失败的回调 onRejected</li>
<li>then 的参数 onFulfilled 和 onRejected 可以缺省，如果 onFulfilled 或者 onRejected 不是函数，将其忽略，且依旧可以在下面的 then 中获取到之前返回的值；</li>
<li>promise 可以 then 多次，每次执行完 promise.then 方法后返回的都是一个<strong>“新的 promise”</strong></li>
<li>如果 then 的返回值是一个普通值，那么就会把这个结果作为参数，传递给下一个 then 的成功的回调中；</li>
<li>如果 then 的<strong>返回值是一个 promise，那么会等这个 promise 执行完</strong>，promise 如果成功，就走下一个 then 的成功；如果失败，就走下一个 then 的失败；如果抛 1. 出异常，就走下一个 then 的失败</li>
<li>如果 then 的返回值和 promise 是同一个引用对象，造成循环引用，则抛出异常，把异常传递给下一个 then 的失败的回调中</li>
<li>如果 then 的返回值 x 是一个 promise，且 x 同时调用 resolve 函数和 reject 函数，则第一次调用优先，其他所有调用被忽略</li>
</ol>
<p><code>Promise实现代码：</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br></pre></td><td class="code"><pre><span class="line">const PENDING = &apos;PENDING&apos;;</span><br><span class="line">const FULFILLED = &apos;FULFILLED&apos;;</span><br><span class="line">const REJECTED = &apos;REJECTED&apos;;</span><br><span class="line"></span><br><span class="line">const resolvePromise = (promise2, x, resolve, reject) =&gt; &#123;</span><br><span class="line">  // 自己等待自己完成是错误的实现，用一个类型错误，结束掉 promise  Promise/A+ 2.3.</span><br><span class="line">  if (promise2 === x) &#123;</span><br><span class="line">    return reject(new TypeError(&apos;Chaining cycle detected for promise #&lt;Promise&gt;&apos;))</span><br><span class="line">  &#125;</span><br><span class="line">  // Promise/A+ 2.3.3.3.3 只能调用一次</span><br><span class="line">  let called;</span><br><span class="line">  // 后续的条件要严格判断 保证代码能和别的库一起使用</span><br><span class="line">  if ((typeof x === &apos;object&apos; &amp;&amp; x != null) || typeof x === &apos;function&apos;) &#123;</span><br><span class="line">    try &#123;</span><br><span class="line">      // 为了判断 resolve 过的就不用再 reject 了（比如 reject 和 resolve 同时调用的时候）  Promise/A+ 2.3.3.1</span><br><span class="line">      let then = x.then;</span><br><span class="line">      if (typeof then === &apos;function&apos;) &#123;</span><br><span class="line">        // 不要写成 x.then，直接 then.call 就可以了 因为 x.then 会再次取值，Object.defineProperty  Promise/A+ 2.3.3.3</span><br><span class="line">        then.call(x, y =&gt; &#123; // 根据 promise 的状态决定是成功还是失败</span><br><span class="line">          if (called) return;</span><br><span class="line">          called = true;</span><br><span class="line">          // 递归解析的过程（因为可能 promise 中还有 promise） Promise/A+ 2.3.3.3.1</span><br><span class="line">          resolvePromise(promise2, y, resolve, reject);</span><br><span class="line">        &#125;, r =&gt; &#123;</span><br><span class="line">          // 只要失败就失败 Promise/A+ 2.3.3.3.2</span><br><span class="line">          if (called) return;</span><br><span class="line">          called = true;</span><br><span class="line">          reject(r);</span><br><span class="line">        &#125;);</span><br><span class="line">      &#125; else &#123;</span><br><span class="line">        // 如果 x.then 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.3.4</span><br><span class="line">        resolve(x);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; catch (e) &#123;</span><br><span class="line">      // Promise/A+ 2.3.3.2</span><br><span class="line">      if (called) return;</span><br><span class="line">      called = true;</span><br><span class="line">      reject(e)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125; else &#123;</span><br><span class="line">    // 如果 x 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.4</span><br><span class="line">    resolve(x)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class Promise &#123;</span><br><span class="line">  constructor(executor) &#123;</span><br><span class="line">    this.status = PENDING;</span><br><span class="line">    this.value = undefined;</span><br><span class="line">    this.reason = undefined;</span><br><span class="line">    this.onResolvedCallbacks = [];</span><br><span class="line">    this.onRejectedCallbacks= [];</span><br><span class="line"></span><br><span class="line">    let resolve = (value) =&gt; &#123;</span><br><span class="line">      if(this.status ===  PENDING) &#123;</span><br><span class="line">        this.status = FULFILLED;</span><br><span class="line">        this.value = value;</span><br><span class="line">        this.onResolvedCallbacks.forEach(fn=&gt;fn());</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    let reject = (reason) =&gt; &#123;</span><br><span class="line">      if(this.status ===  PENDING) &#123;</span><br><span class="line">        this.status = REJECTED;</span><br><span class="line">        this.reason = reason;</span><br><span class="line">        this.onRejectedCallbacks.forEach(fn=&gt;fn());</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    try &#123;</span><br><span class="line">      executor(resolve,reject)</span><br><span class="line">    &#125; catch (error) &#123;</span><br><span class="line">      reject(error)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&lt;!-- 静态方法 --&gt;</span><br><span class="line">  static resolve(data)&#123;</span><br><span class="line">    return new Promise((resolve,reject)=&gt;&#123;</span><br><span class="line">      resolve(data);</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  static reject(reason)&#123;</span><br><span class="line">    return new Promise((resolve,reject)=&gt;&#123;</span><br><span class="line">      reject(reason);</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  static race(promises) &#123;</span><br><span class="line">    return new Promise((resolve, reject) =&gt; &#123;</span><br><span class="line">      // 一起执行就是for循环</span><br><span class="line">      for (let i = 0; i &lt; promises.length; i++) &#123;</span><br><span class="line">        let val = promises[i];</span><br><span class="line">        if (val &amp;&amp; typeof val.then === &apos;function&apos;) &#123;</span><br><span class="line">          val.then(resolve, reject);</span><br><span class="line">        &#125; else &#123; // 普通值</span><br><span class="line">          resolve(val)</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  static all(values) &#123;</span><br><span class="line">    if (!Array.isArray(values)) &#123;</span><br><span class="line">      const type = typeof values;</span><br><span class="line">      return new TypeError(`TypeError: $&#123;type&#125; $&#123;values&#125; is not iterable`)</span><br><span class="line">    &#125;</span><br><span class="line">    return new Promise((resolve, reject) =&gt; &#123;</span><br><span class="line">      let resultArr = [];</span><br><span class="line">      let orderIndex = 0;</span><br><span class="line">      const processResultByKey = (value, index) =&gt; &#123;</span><br><span class="line">        resultArr[index] = value;</span><br><span class="line">        if (++orderIndex === values.length) &#123;</span><br><span class="line">            resolve(resultArr)</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      for (let i = 0; i &lt; values.length; i++) &#123;</span><br><span class="line">        let value = values[i];</span><br><span class="line">        if (value &amp;&amp; typeof value.then === &apos;function&apos;) &#123;</span><br><span class="line">          value.then((value) =&gt; &#123;</span><br><span class="line">            processResultByKey(value, i);</span><br><span class="line">          &#125;, reject);</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">          processResultByKey(value, i);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  then(onFulfilled, onRejected) &#123;</span><br><span class="line">    //解决 onFufilled，onRejected 没有传值的问题</span><br><span class="line">    //Promise/A+ 2.2.1 / Promise/A+ 2.2.5 / Promise/A+ 2.2.7.3 / Promise/A+ 2.2.7.4</span><br><span class="line">    onFulfilled = typeof onFulfilled === &apos;function&apos; ? onFulfilled : v =&gt; v;</span><br><span class="line">    //因为错误的值要让后面访问到，所以这里也要跑出个错误，不然会在之后 then 的 resolve 中捕获</span><br><span class="line">    onRejected = typeof onRejected === &apos;function&apos; ? onRejected : err =&gt; &#123; throw err &#125;;</span><br><span class="line">    // 每次调用 then 都返回一个新的 promise  Promise/A+ 2.2.7</span><br><span class="line">    let promise2 = new Promise((resolve, reject) =&gt; &#123;</span><br><span class="line">      if (this.status === FULFILLED) &#123;</span><br><span class="line">        //Promise/A+ 2.2.2</span><br><span class="line">        //Promise/A+ 2.2.4 --- setTimeout</span><br><span class="line">        setTimeout(() =&gt; &#123;</span><br><span class="line">          try &#123;</span><br><span class="line">            //Promise/A+ 2.2.7.1</span><br><span class="line">            let x = onFulfilled(this.value);</span><br><span class="line">            // x可能是一个proimise</span><br><span class="line">            resolvePromise(promise2, x, resolve, reject);</span><br><span class="line">          &#125; catch (e) &#123;</span><br><span class="line">            //Promise/A+ 2.2.7.2</span><br><span class="line">            reject(e)</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;, 0);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      if (this.status === REJECTED) &#123;</span><br><span class="line">        //Promise/A+ 2.2.3</span><br><span class="line">        setTimeout(() =&gt; &#123;</span><br><span class="line">          try &#123;</span><br><span class="line">            let x = onRejected(this.reason);</span><br><span class="line">            resolvePromise(promise2, x, resolve, reject);</span><br><span class="line">          &#125; catch (e) &#123;</span><br><span class="line">            reject(e)</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;, 0);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      if (this.status === PENDING) &#123;</span><br><span class="line">        this.onResolvedCallbacks.push(() =&gt; &#123;</span><br><span class="line">          setTimeout(() =&gt; &#123;</span><br><span class="line">            try &#123;</span><br><span class="line">              let x = onFulfilled(this.value);</span><br><span class="line">              resolvePromise(promise2, x, resolve, reject);</span><br><span class="line">            &#125; catch (e) &#123;</span><br><span class="line">              reject(e)</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;, 0);</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        this.onRejectedCallbacks.push(()=&gt; &#123;</span><br><span class="line">          setTimeout(() =&gt; &#123;</span><br><span class="line">            try &#123;</span><br><span class="line">              let x = onRejected(this.reason);</span><br><span class="line">              resolvePromise(promise2, x, resolve, reject)</span><br><span class="line">            &#125; catch (e) &#123;</span><br><span class="line">              reject(e)</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;, 0);</span><br><span class="line">        &#125;);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    return promise2;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Promise.prototype.catch = function(errCallback)&#123;</span><br><span class="line">  return this.then(null,errCallback)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Promise.prototype.finally = function(callback) &#123;</span><br><span class="line">  return this.then((value)=&gt;&#123;</span><br><span class="line">    return Promise.resolve(callback()).then(()=&gt;value)</span><br><span class="line">  &#125;,(reason)=&gt;&#123;</span><br><span class="line">    return Promise.resolve(callback()).then(()=&gt;&#123;throw reason&#125;)</span><br><span class="line">  &#125;)  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://zhuanlan.zhihu.com/p/183801144" target="_blank" rel="noopener">你能手写一个 Promise 吗</a></p>

      
    </div>
    <footer class="article-footer">
      <!--<a data-url="http://cai4633.github.io/2020/08/18/一篇文章搞懂promise/" data-id="ckjqth4rz002i2gw03ckvb48w" class="article-share-link">Share</a>-->
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/promise/">promise</a></li></ul>

    </footer>
  </div>
  <!---->
</article>


  
    <article id="post-防抖和节流" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/07/18/防抖和节流/" class="article-date">
  <time datetime="2020-07-18T02:56:17.000Z" itemprop="datePublished">2020-07-18</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/前端/">前端</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/07/18/防抖和节流/">防抖和节流</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>在前端开发的过程中，我们经常会遇到一些的事件会持续触发，如 resize、scroll、mousemove 等，但有时候我们并不希望事件函数频繁的被执行。这时就需要用到<strong>防抖和节流</strong></p>
<h2 id="防抖（控制次数）"><a href="#防抖（控制次数）" class="headerlink" title="防抖（控制次数）"></a>防抖（控制次数）</h2><p><strong>防抖，就是触发事件后在 n 秒内函数只能执行一次，如果在 n 秒内又触发了事件，则会重新计时。</strong> 常见的实现方法如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">function debounce (fn,  delay) &#123;</span><br><span class="line">  let timer = null</span><br><span class="line">  return function(...args)&#123;</span><br><span class="line">    if(timer)&#123;</span><br><span class="line">      window.clearTimeout(timer)</span><br><span class="line">    &#125;</span><br><span class="line">    timer = setTimeout(()=&gt;&#123;</span><br><span class="line">      fn.apply(this,args)</span><br><span class="line">    &#125;,delay)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="节流（控制频率）"><a href="#节流（控制频率）" class="headerlink" title="节流（控制频率）"></a>节流（控制频率）</h2><p><strong>节流，就是事件连续触发时，控制在 n 秒中只执行一次函数，如果在 n 秒内又触发了事件,会忽略触发</strong>。常见的实现方法如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">function throttle(fn, delay) &#123;</span><br><span class="line">  let timer = null</span><br><span class="line">  return function (...arg)&#123;</span><br><span class="line">    if(!timer)&#123;</span><br><span class="line">      fn.apply(this, arg)</span><br><span class="line">      timer = window.setTimeout(()=&gt;&#123;</span><br><span class="line">        window.clearTimeout(timer)</span><br><span class="line">        timer = null</span><br><span class="line">      &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="防抖和节流的区别"><a href="#防抖和节流的区别" class="headerlink" title="防抖和节流的区别"></a>防抖和节流的区别</h2><p>有时候我们很容易混淆防抖和节流的触发机制，下面我们用一张图来展示他们的区别。<br><img src="/images/%E9%98%B2%E6%8A%96%E5%92%8C%E8%8A%82%E6%B5%81%E7%9A%84%E5%8C%BA%E5%88%AB.png" alt="防抖和节流的区别"></p>

      
    </div>
    <footer class="article-footer">
      <!--<a data-url="http://cai4633.github.io/2020/07/18/防抖和节流/" data-id="ckjqth4sz003q2gw04dxy37or" class="article-share-link">Share</a>-->
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/精华/">精华</a></li></ul>

    </footer>
  </div>
  <!---->
</article>


  
    <article id="post-vue-cli本地代理mock数据" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/05/25/vue-cli本地代理mock数据/" class="article-date">
  <time datetime="2020-05-25T14:33:10.000Z" itemprop="datePublished">2020-05-25</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/前端/">前端</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/05/25/vue-cli本地代理mock数据/">vue-cli本地代理mock数据</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>近期在做一个项目需要从qq音乐服务器抓取数据，但是由于浏览器同源策略的限制导致获取数据失败，需要从本地nodejs服务器中代理抓取数据，下面是vue-cli中的常见的配置。</p>
<h2 id="vue-cli-2-0之前的版本配置"><a href="#vue-cli-2-0之前的版本配置" class="headerlink" title="vue-cli 2.0之前的版本配置"></a>vue-cli 2.0之前的版本配置</h2><p>找到<strong>build/dev-server.js</strong>文件，加入以下代码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">var axios = require(&apos;axios&apos;)</span><br><span class="line">var app = express()</span><br><span class="line">var apiRoutes = express.Router()</span><br><span class="line"></span><br><span class="line">apiRoutes.get(&apos;/getSongs&apos;, (req, res) =&gt; &#123;</span><br><span class="line">  var url = &apos;http://www.baidu.com&apos;</span><br><span class="line">  axios.get(url, &#123;</span><br><span class="line">    headers: &#123;</span><br><span class="line">      referer: &apos;http://www.baidu.com&apos;,</span><br><span class="line">      host: &apos;http://www.baidu.com&apos;</span><br><span class="line">    &#125;,</span><br><span class="line">    params: req.query</span><br><span class="line">  &#125;).then((response) =&gt; &#123;</span><br><span class="line">    res.json(response.data)</span><br><span class="line">  &#125;).catch((e) =&gt; &#123;</span><br><span class="line">    console.log(e)</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">app.use(&apos;/api&apos;, apiRoutes)</span><br></pre></td></tr></table></figure>

<hr>
<h2 id="vue-cli-3-0-版本配置"><a href="#vue-cli-3-0-版本配置" class="headerlink" title="vue-cli 3.0+版本配置"></a>vue-cli 3.0+版本配置</h2><p>3.0+版本的vue已将dev-server.js与webpack.dev.conf.js合并，若要写路由相关配置需要找到webpack.dev.conf.js中的<strong>devServer对象</strong>进行相关配置</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">const axios = require(&quot;axios&quot;)</span><br><span class="line">const express = require(&quot;express&quot;)</span><br><span class="line">let app = express()</span><br><span class="line">let apiRoutes = express.Router()</span><br><span class="line">app.use(&quot;/api&quot;, apiRoutes)</span><br><span class="line"></span><br><span class="line">devServer: &#123;</span><br><span class="line">        before(app) &#123;</span><br><span class="line">            app.get(&quot;/api/getSongs&quot;, function(req, res) &#123;</span><br><span class="line">                var url = &quot;http://www.baidu.com&quot;</span><br><span class="line">                axios.get(url).then((response) =&gt; &#123; res.json(response.data) &#125;) .catch((e) =&gt; &#123; console.log(e) &#125;) &#125;)</span><br><span class="line">        &#125;,</span><br><span class="line">    &#125;,</span><br></pre></td></tr></table></figure>

<hr>
<h2 id="vue-cli-4-0-版本配置"><a href="#vue-cli-4-0-版本配置" class="headerlink" title="vue-cli 4.0+版本配置"></a>vue-cli 4.0+版本配置</h2><p>4.0+版本的vue-cli配置藏的更深，需要找到<code>node_modules/@vue/cli-service/options.js</code>文件中的<strong>devServer</strong>对象进行配置，配置方法同上面3.0版本类似。另外我们还可以在项目根目录自己新建一个vue.config.js配置文件，新建devServer对象利用<strong>before(app)或者proxy</strong>来进行请求代理。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">const path = require(&quot;path&quot;)</span><br><span class="line">function resolve(dir) &#123;</span><br><span class="line">    return path.join(__dirname, dir)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">const axios = require(&quot;axios&quot;)</span><br><span class="line">const express = require(&quot;express&quot;)</span><br><span class="line">let app = express()</span><br><span class="line">let apiRoutes = express.Router()</span><br><span class="line">app.use(&quot;/api&quot;, apiRoutes)</span><br><span class="line"></span><br><span class="line">module.exports = &#123;</span><br><span class="line">    chainWebpack: (config) =&gt; &#123;</span><br><span class="line">        config.resolve.alias</span><br><span class="line">            .set(&quot;@&quot;, resolve(&quot;src&quot;))</span><br><span class="line">            .set(&quot;assets&quot;, resolve(&quot;src/assets&quot;))</span><br><span class="line">            .set(&quot;components&quot;, resolve(&quot;src/components&quot;))</span><br><span class="line">            .set(&quot;base&quot;, resolve(&quot;baseConfig&quot;))</span><br><span class="line">            .set(&quot;public&quot;, resolve(&quot;public&quot;))</span><br><span class="line">    &#125;,</span><br><span class="line">    devServer: &#123;</span><br><span class="line">        before(app) &#123;</span><br><span class="line">            app.get(&quot;/api/lyric&quot;, function(req, res) &#123;</span><br><span class="line">                var url = &quot;http://www.baidu.com&quot;</span><br><span class="line">                axios</span><br><span class="line">                    .get(url)</span><br><span class="line">                    .then((response) =&gt; &#123;</span><br><span class="line">                        res.json(response.data)</span><br><span class="line">                    &#125;)</span><br><span class="line">                    .catch((e) =&gt; &#123;</span><br><span class="line">                        console.log(e)</span><br><span class="line">                    &#125;)</span><br><span class="line">            &#125;)</span><br><span class="line">        &#125;,</span><br><span class="line">        // proxy: &#123;</span><br><span class="line">        //     &quot;/api/getLists&quot;: &#123;</span><br><span class="line">        //         target:</span><br><span class="line">        //             &quot;http://www.baidu.com&quot;, //代理接口</span><br><span class="line">        //         changeOrigin: true,</span><br><span class="line">        //         pathRewrite: &#123;</span><br><span class="line">        //          &apos;^/api&apos;: &apos;&apos;    //代理的路径 //是否移除api三个字段,</span><br><span class="line">        //         &#125;</span><br><span class="line">        //         headers: &#123;</span><br><span class="line">        //             Referer: &quot;https://www.baidu.com&quot;,</span><br><span class="line">        //         &#125;,</span><br><span class="line">        //     &#125;,</span><br><span class="line">        // &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="nodejs使用res-json-返回响应时，如果直接传入response变量会引起循环引用报错-只传入response-data就不会出现报错了。"><a href="#nodejs使用res-json-返回响应时，如果直接传入response变量会引起循环引用报错-只传入response-data就不会出现报错了。" class="headerlink" title="nodejs使用res.json()返回响应时，如果直接传入response变量会引起循环引用报错,只传入response.data就不会出现报错了。"></a>nodejs使用res.json()返回响应时，如果直接传入<code>response</code>变量会引起循环引用报错,只传入<code>response.data</code>就不会出现报错了。</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">TypeError: Converting circular structure to JSON</span><br><span class="line">    --&gt; starting at object with constructor &apos;ClientRequest&apos;</span><br><span class="line">    |     property &apos;socket&apos; -&gt; object with constructor &apos;Socket&apos;</span><br><span class="line">    --- property &apos;_httpMessage&apos; closes the circle</span><br><span class="line">    at JSON.stringify (&lt;anonymous&gt;)</span><br><span class="line">    at stringify (...\node_modules\express\lib\response.js:1123:12)</span><br><span class="line">    at ServerResponse.json (...\node_modules\express\lib\response.js:260:14)</span><br><span class="line">    at ...\vue.config.js:37:41</span><br><span class="line">    at processTicksAndRejections (internal/process/task_queues.js:97:5)</span><br></pre></td></tr></table></figure>


      
    </div>
    <footer class="article-footer">
      <!--<a data-url="http://cai4633.github.io/2020/05/25/vue-cli本地代理mock数据/" data-id="ckjqth4si002w2gw0rhuijmgv" class="article-share-link">Share</a>-->
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/devServer/">devServer</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/proxy/">proxy</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/vue-cli/">vue-cli</a></li></ul>

    </footer>
  </div>
  <!---->
</article>


  
    <article id="post-仿网易云音乐遇到的坑" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/05/21/仿网易云音乐遇到的坑/" class="article-date">
  <time datetime="2020-05-21T05:57:58.000Z" itemprop="datePublished">2020-05-21</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/前端项目/">前端项目</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/05/21/仿网易云音乐遇到的坑/">仿网易云音乐遇到的坑</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>接近一个月没写博客了，这段时间主要在练习一个小项目-<a href="http://caijd.top/163music/src/index.html" target="_blank" rel="noopener">仿网易云音乐</a>。这个项目使用<strong>原生JS和jquery</strong>构建，支持移动端,支持歌单、最新和最热歌曲点播，播放页支持歌词。后台通过leancloud 提供的API搭建。通过这个小项目，我对前后端交互有了更深刻的认识。同时对<strong>MVC和OOP思想</strong>也有了自己的理解。今天我对这段时间学到的新知识和遇到的一些BUG记录下来，以便以后借鉴。</p>
<h2 id="safari浏览器中的奇怪行为"><a href="#safari浏览器中的奇怪行为" class="headerlink" title="safari浏览器中的奇怪行为"></a>safari浏览器中的奇怪行为</h2><ol>
<li>在safari中不能<strong>使用js在body中</strong>动态添加<code>background-image:url()</code>。否则会出现body背景图片显示不全的BUG。而其他的浏览器目前没有发现这个问题。</li>
</ol>
<p><strong>解决方法：</strong>可以在body中新建一个<code>div</code>元素专门用来作为<code>background-image</code>的容器</p>
<hr>
<ol start="2">
<li>safari暂时不支持<code>image/webp</code>图片格式<br>现在很多公司为了优化web性能，网页图片都使用google定义的image/webp图片标准。目前chrome、firefox等主流的浏览器都支持它，但safari和IE由于各种原因暂时不支持image/webp。当我们使用webp图片时就会出现图片不显示的问提。所以我们在使用webp之前需要进行浏览器功能检查。</li>
</ol>
<p>方法1：尝试加载一张WebP图片，观察是否能够正常加载 </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    let WebP = new Image();</span><br><span class="line">    WebP.onload = ()=&gt;&#123;     //支持webp       &#125;    </span><br><span class="line">    WebP.onerror = ()=&gt;&#123;    //不支持webp     &#125;</span><br><span class="line">    WebP.src = &apos;图片url&apos;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>方法2：通过canvas中特性来判断<br><code>document.createElement(&#39;canvas&#39;).toDataURL(&#39;image/webp&#39;,0.5).indexOf(&#39;data:image/webp&#39;) === 0</code><br>canvas.toDataURL()默认输出<strong>image/png</strong>格式，当支持webp时，上面的表达式输出<strong>image/webp</strong></p>
<hr>
<h2 id="事件中心eventHub和url查询参数"><a href="#事件中心eventHub和url查询参数" class="headerlink" title="事件中心eventHub和url查询参数"></a>事件中心eventHub和url查询参数</h2><p>前端一直在朝着模块化的方向发展，模块的一个功能就是封装方法和属性。那么模块间的数据传递就是摆在我们的眼前的一个难题。模块数据传递目前分为同一页面的数据传递和不同页面的数据传递。</p>
<h3 id="同一页面数据的传递"><a href="#同一页面数据的传递" class="headerlink" title="同一页面数据的传递"></a>同一页面数据的传递</h3><p>如果多个模块在同一个页面，那么我们可以利用<strong>事件中心eventHub</strong>，通过<strong>发布（emit）和订阅（on）</strong>思想来实现数据的传递。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">window.eventHub = &#123;</span><br><span class="line">  events: &#123;&#125;,</span><br><span class="line">  emit(event,data)&#123;</span><br><span class="line">    if(this.events[event] &amp;&amp; this.events[event].length)&#123;</span><br><span class="line">      this.events[event].forEach((fn)=&gt;&#123;</span><br><span class="line">        fn.call(null,data) </span><br><span class="line">      &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  on(event,fn)&#123;</span><br><span class="line">    if(Array.isArray(event))&#123;</span><br><span class="line">      event.forEach((item)=&gt;&#123;</span><br><span class="line">        this.events[item] = this.events[item] || []</span><br><span class="line">        this.events[item].push(fn)</span><br><span class="line">      &#125;)</span><br><span class="line">    &#125;else&#123;</span><br><span class="line">      this.events[event] = this.events[event] || []</span><br><span class="line">      this.events[event].push(fn)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<hr>
<h3 id="不同页面间的数据传递"><a href="#不同页面间的数据传递" class="headerlink" title="不同页面间的数据传递"></a>不同页面间的数据传递</h3><p>当多个模块不在同一页面时，eventHub没有了全局的基础，这时候我们可以通过<strong>http请求URL的query参数</strong>(?id=1111&amp;name=cai)来实现数据的传递。或者使用更高级的<strong>cookie ,sessionID以及localStorage</strong>。</p>

      
    </div>
    <footer class="article-footer">
      <!--<a data-url="http://cai4633.github.io/2020/05/21/仿网易云音乐遇到的坑/" data-id="ckjqth4rz002k2gw05qk89xh1" class="article-share-link">Share</a>-->
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/jquery/">jquery</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/js/">js</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/leancloud/">leancloud</a></li></ul>

    </footer>
  </div>
  <!---->
</article>


  
    <article id="post-浏览器同源策略与跨域" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/04/19/浏览器同源策略与跨域/" class="article-date">
  <time datetime="2020-04-19T14:27:25.000Z" itemprop="datePublished">2020-04-19</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/前端/">前端</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/04/19/浏览器同源策略与跨域/">浏览器同源策略与跨域</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="同源策略"><a href="#同源策略" class="headerlink" title="同源策略"></a>同源策略</h2><p>只有<strong>协议、域名和端口号完全一致</strong>的两个网站，才可以资源共享。其中受限制的行为包括：</p>
<ol>
<li>AJAX 请求不能发送。（发送了得不到响应）</li>
<li>Cookie、LocalStorage 和 IndexDB 无法读取。</li>
<li>DOM 无法获得</li>
</ol>
<p>同源策略的目的，是为了保证用户信息的安全。</p>
<hr>
<h2 id="JSONP"><a href="#JSONP" class="headerlink" title="JSONP"></a>JSONP</h2><h3 id="HTML可以发送请求的元素"><a href="#HTML可以发送请求的元素" class="headerlink" title="HTML可以发送请求的元素"></a>HTML可以发送请求的元素</h3><ol>
<li><p>form表单和a标签(都会刷新页面,体验不好)</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&lt;form method=&apos;get&apos; action=&apos;/path&apos; target=&apos;&apos;&gt;&lt;/form&gt;     //将target指向特定的iframe可以间接消除浏览器刷新</span><br><span class="line">&lt;a href=&apos;/path&apos;&gt;&lt;/a&gt;</span><br></pre></td></tr></table></figure>
</li>
<li><p>img/link元素</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">let img = document.createElement(&apos;img&apos;)</span><br><span class="line">img.src = &apos;/path&apos;</span><br><span class="line">img.onload = ()=&gt;&#123; ...codeBlock &#125;   //必须返回一个图片才会onload</span><br><span class="line">img.onerror = ()=&gt;&#123; ...codeBlock &#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>script元素<br><img src="/images/SRJ.png" alt="server rendered javascript"></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">let script = document.createElement(&apos;script&apos;)</span><br><span class="line">script.src = &apos;/path&apos;</span><br><span class="line">document.body.appendChild(&apos;script&apos;) //必须将script加入到dom中，请求才生效</span><br><span class="line">script.onload = (e)=&gt;&#123; ...codeBlock e.currentTarget.remove() &#125;   //先执行服务器返回文本再执行onload回调</span><br><span class="line">script.onerror = ()=&gt;&#123; ...codeBlock &#125;</span><br></pre></td></tr></table></figure>

</li>
</ol>
<hr>
<h3 id="JSONP（json-with-padding）"><a href="#JSONP（json-with-padding）" class="headerlink" title="JSONP（json with padding）"></a>JSONP（json with padding）</h3><p>如同上面方法3，JSONP基本思想是,<strong>动态添加一个script元素</strong>，向服务器请求数据，这种做法不受同源政策限制；服务器收到请求后，将数据放在一个指定名字的回调函数里传回来。</p>
<h2 id><a href="#" class="headerlink" title></a><img src="/images/jsonp.png" alt="jsonp小结"></h2><h4 id="jsonp注意点"><a href="#jsonp注意点" class="headerlink" title="jsonp注意点"></a>jsonp注意点</h4><ol>
<li>jsonp只能使用get请求</li>
<li>jsonp中script标签src传入的查询参数约定为<code>?callback=funcName</code>,其中funcName由字母和<strong>随机数</strong>构成</li>
<li>服务器通过<strong>回调函数的参数</strong>给浏览器传递任意数据</li>
</ol>
<hr>
<h2 id="AJAX（async-javascript-and-XML）和-CORS-cross-origi-resource-sharing"><a href="#AJAX（async-javascript-and-XML）和-CORS-cross-origi-resource-sharing" class="headerlink" title="AJAX（async javascript and XML）和 CORS(cross-origi resource sharing)"></a>AJAX（async javascript and XML）和 CORS(cross-origi resource sharing)</h2><p>早期的前端只能发送简单的get请求。IE 5率先在 JS 中引入<strong>ActiveX</strong>对象，使得 JS 可以直接发起 HTTP 请求（get post put delete）。随后 Mozilla、 Safari、 Opera 也跟进引入了<strong>XMLHttpRequest</strong>对象，最后<strong>XMLHttpRequest</strong>对象被纳入 W3C 规范。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- 前端代码 --&gt;</span><br><span class="line">let xhr = new XMLHttpRequest()</span><br><span class="line">xhr.open(&apos;post&apos;,&apos;www.baidu.com&apos;)        // 打开xhr ，并设置mehtod 和 path</span><br><span class="line">xhr.setRequestHeader(&apos;content-type&apos;,&apos;text/javascript;charset=utf8&apos;) //设置请求头</span><br><span class="line">xhr.onreadystatechange = (e)=&gt;&#123;</span><br><span class="line">    if(xhr.readystate === 4)&#123;</span><br><span class="line">        if(xhr.status &gt;=200 &amp;&amp; xhr.status &lt;300)&#123;</span><br><span class="line">            console.log(&apos;请求成功！&apos;)</span><br><span class="line">            let response = xhr.responseText //获取响应字符串</span><br><span class="line">            let json = JSON.parse(response) //将字符串转化为js对象</span><br><span class="line">            ...</span><br><span class="line">        &#125;</span><br><span class="line">        else if(xhr.status &gt;=400)&#123;</span><br><span class="line">            console.log(&apos;请求失败！&apos;)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">xhr.send()      //添加参数可以设置request body</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&lt;!-- 后端代码 --&gt;</span><br><span class="line">if(path === &apos;/path&apos;)&#123;</span><br><span class="line">    response.statusCode = 200</span><br><span class="line">    response.setHeader(&apos;Content-Type&apos;,&apos;text/json;charset=utf-8&apos;)</span><br><span class="line">    response.setHeader(&apos;Access-Control-Allow-Origin&apos;,&apos;http://www.baidu.com:8080&apos;)       //CORS</span><br><span class="line">    response.setHeader(&apos;Access-Control-Allow-Origin&apos;,&apos;*&apos;)   //CORS</span><br><span class="line">    response.write(</span><br><span class="line">        `&#123;</span><br><span class="line">            &quot;a&quot;: 1,</span><br><span class="line">            &quot;b&quot;: 2</span><br><span class="line">        &#125;</span><br><span class="line">        `</span><br><span class="line">    )    </span><br><span class="line">    response.end()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<hr>
<h3 id="AJAX常用的API"><a href="#AJAX常用的API" class="headerlink" title="AJAX常用的API"></a>AJAX常用的API</h3><table>
<thead>
<tr>
<th>API</th>
<th>usage</th>
</tr>
</thead>
<tbody><tr>
<td>xhr.readystate</td>
<td>0-close、1-open、 2-send and receive responseHeader responseStatus、3-download and loading、 4-completed</td>
</tr>
<tr>
<td>xhr.status</td>
<td>状态码 200 400</td>
</tr>
<tr>
<td>xhr.statusText</td>
<td>状态码文本 ok</td>
</tr>
<tr>
<td>xhr.responseText</td>
<td>响应</td>
</tr>
<tr>
<td>xhr.getAllResponseHeaders()</td>
<td>获取所有的响应头</td>
</tr>
<tr>
<td>xhr.getAllResponseHeader(‘’)</td>
<td>获取响应头</td>
</tr>
<tr>
<td>xhr.setRequestHeader(‘’)</td>
<td>设置请求头</td>
</tr>
<tr>
<td>JSON.parse(‘’)</td>
<td>string to json</td>
</tr>
<tr>
<td>JSON.stringify(obj)</td>
<td>json to string</td>
</tr>
</tbody></table>
<hr>
<h3 id="CORS"><a href="#CORS" class="headerlink" title="CORS"></a>CORS</h3><p>由于AJAX存在<strong>同源限制</strong>，所以出现了CORS。CORS就是在服务器端加上响应头。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">response.setHeader(&quot;Access-Control-Allow-Origin&quot;,path)</span><br></pre></td></tr></table></figure>


      
    </div>
    <footer class="article-footer">
      <!--<a data-url="http://cai4633.github.io/2020/04/19/浏览器同源策略与跨域/" data-id="ckjqth4si00302gw0ryndq55x" class="article-share-link">Share</a>-->
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/CORS/">CORS</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/JSONP/">JSONP</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/ajax/">ajax</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/同源/">同源</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/浏览器/">浏览器</a></li></ul>

    </footer>
  </div>
  <!---->
</article>


  
    <article id="post-浏览器的http请求过程" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/04/19/浏览器的http请求过程/" class="article-date">
  <time datetime="2020-04-19T05:05:17.000Z" itemprop="datePublished">2020-04-19</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/前端/">前端</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/04/19/浏览器的http请求过程/">浏览器的http请求过程</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="浏览器从URL到页面显示的过程"><a href="#浏览器从URL到页面显示的过程" class="headerlink" title="浏览器从URL到页面显示的过程"></a>浏览器从URL到页面显示的过程</h2><p><img src="/images/browser.png" alt="浏览器http请求过程"></p>
<ol>
<li>域名解析，返回IP</li>
<li>发起TCP的3次握手，建立TCP连接</li>
<li>浏览器发起http请求，服务器响应htp请求</li>
<li>浏览器解析html代码,并请求html代码中的资源(如js、css、图片等）</li>
<li>4次挥手,断开TCP连接</li>
<li>浏览器对渲染页面呈现给用户</li>
</ol>
<h3 id="域名解析"><a href="#域名解析" class="headerlink" title="域名解析"></a>域名解析</h3><p>域名解析包括<strong>本地DNS缓存查询和远程DNS查询</strong>。</p>
<p>本地缓存查询按照浏览器DNS缓存 → 操作系统DNS缓存 → HOSTS文件的顺序依次查找，如果没有找到对应的IP，再进行远程DNS查询。<br>远程DNS查询首先在本地域名服务器（网络接入商提供）缓存上查询，如果没有结果再依次按照根域名服务器 → 顶级域名服务器（.com顶级域名） → 权威域名服务器(二级域名和三级域名)的顺序依次查询，并<strong>将IP依次返回给本地域名服务器、操作系统和浏览器</strong>。</p>
<h3 id="TCP连接"><a href="#TCP连接" class="headerlink" title="TCP连接"></a>TCP连接</h3><h4 id="三次握手建立连接"><a href="#三次握手建立连接" class="headerlink" title="三次握手建立连接"></a>三次握手建立连接</h4><p><img src="/images/tcp_connect.png" alt="tcp三次握手"></p>
<table>
<thead>
<tr>
<th>FLAGS</th>
<th>meaning</th>
</tr>
</thead>
<tbody><tr>
<td>SYN</td>
<td>synchronous 表示请求建立连接，并在其序列号的字段进行序列号的初始值设定。建立连接，一般设置为1</td>
</tr>
<tr>
<td>seq</td>
<td>sequence 随机序列码</td>
</tr>
<tr>
<td>ACK</td>
<td>Acknowledgement 确认，表示响应</td>
</tr>
<tr>
<td>ACKFLAG</td>
<td>确认标志，一般设为1</td>
</tr>
<tr>
<td>ACKNumber</td>
<td>确认号码，表示期待下一次接受的包序号</td>
</tr>
<tr>
<td>PSH</td>
<td>push 推送，提示接收端应用程序立即从TCP缓冲区把数据读走</td>
</tr>
<tr>
<td>FIN</td>
<td>finish 完成，希望断开连接</td>
</tr>
<tr>
<td>RST</td>
<td>reset 复位，对方要求重新建立连接</td>
</tr>
<tr>
<td>URG</td>
<td>urgent 紧急指针是否有效。为1，表示某一位需要被优先处理</td>
</tr>
</tbody></table>
<p>在TCP/IP协议中，TCP协议提供可靠的连接服务，采用三次握手建立一个连接。<br>第一次握手：建立连接时，客户端发送<strong>SYN包</strong>(<strong>SYN=1</strong>)到服务器，客户端把这段连接的序号<strong>seq设定为随机数x</strong>,进入<strong>SYN_SEND</strong>状态，等待服务器确认；<br>第二次握手：服务器端应当为一个合法的SYN回送一个<strong>SYN/ACK</strong>。ACK的确认码<strong>ACKFLAG=1,ACKNumber=x+1</strong>，SYN/ACK包本身又有一个随机序号<strong>seq=y</strong>此时服务器进入<strong>SYN_RECV</strong>状态；<br>第三次握手：最后，客户端再发送一个<strong>ACK</strong>。此时包的序号<strong>seq被设定为x+1</strong>，而ACK确认码则为<strong>ACKFLAG=1,ACKNumber=y+1</strong>。当服务端收到这个ACK的时候，客户端和服务器进入ESTABLISHED状态，完成了三次握手，客户端与服务器开始传送数据。</p>
<h4 id="四次挥手关闭连接"><a href="#四次挥手关闭连接" class="headerlink" title="四次挥手关闭连接"></a>四次挥手关闭连接</h4><p><img src="/images/tcp_close.png" alt="tcp四次挥手"><br>连接终止使用了四次挥手过程（four-way handshake），在这个过程中连接的<strong>每一侧都独立地被终止</strong>。当一个端点要停止它这一侧的连接，就向对侧发送FIN，对侧回复ACK表示确认。因此，拆掉一侧的连接过程需要一对FIN和ACK，分别由两侧端点发出。</p>
<ol>
<li>客户端进程发出连接释放报文，并且停止发送数据。释放数据报文首部，FIN=1，其序列号为seq=u（等于前面已经传送过来的数据的最后一个字节的序号加1），此时，客户端进入FIN-WAIT-1（终止等待1）状态。 TCP规定，FIN报文段即使不携带数据，也要消耗一个序号。</li>
<li>服务器收到连接释放报文，发出确认报文，ACK=1，ack=u+1，并且带上自己的序列号seq=v，此时，服务端就进入了CLOSE-WAIT（关闭等待）状态。TCP服务器通知高层的应用进程，客户端向服务器的方向就释放了，这时候处于半关闭状态，即客户端已经没有数据要发送了，但是服务器若发送数据，客户端依然要接受。这个状态还要持续一段时间，也就是整个CLOSE-WAIT状态持续的时间。</li>
<li>客户端收到服务器的确认请求后，此时，客户端就进入FIN-WAIT-2（终止等待2）状态，等待服务器发送连接释放报文（在这之前还需要接受服务器发送的最后的数据）。</li>
<li>服务器将最后的数据发送完毕后，就向客户端发送连接释放报文，FIN=1，ack=u+1，由于在半关闭状态，服务器很可能又发送了一些数据，假定此时的序列号为seq=w，此时，服务器就进入了LAST-ACK（最后确认）状态，等待客户端的确认。</li>
<li>客户端收到服务器的连接释放报文后，必须发出确认，ACK=1，ack=w+1，而自己的序列号是seq=u+1，此时，客户端就进入了TIME-WAIT（时间等待）状态。注意此时TCP连接还没有释放，必须经过2∗∗MSL（最长报文段寿命）的时间后，当客户端撤销相应的TCB后，才进入CLOSED状态。</li>
<li>服务器只要收到了客户端发出的确认，立即进入CLOSED状态。同样，撤销TCB后，就结束了这次的TCP连接。可以看到，服务器结束TCP连接的时间要比客户端早一些。</li>
</ol>
<h2 id="参考文献："><a href="#参考文献：" class="headerlink" title="参考文献："></a>参考文献：</h2><p><a href="https://blog.csdn.net/qq_38950316/article/details/81087809" target="_blank" rel="noopener">TCP的三次握手与四次挥手理解及面试题</a><br><a href="https://zh.wikipedia.org/wiki/%E4%BC%A0%E8%BE%93%E6%8E%A7%E5%88%B6%E5%8D%8F%E8%AE%AE" target="_blank" rel="noopener">传输控制协议 wiki</a></p>

      
    </div>
    <footer class="article-footer">
      <!--<a data-url="http://cai4633.github.io/2020/04/19/浏览器的http请求过程/" data-id="ckjqth4si00332gw0b0u9c7ed" class="article-share-link">Share</a>-->
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/http/">http</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/浏览器/">浏览器</a></li></ul>

    </footer>
  </div>
  <!---->
</article>


  


  <nav id="page-nav">
    
    <a class="extend prev" rel="prev" href="/">&laquo; 上一页</a><a class="page-number" href="/">1</a><span class="page-number current">2</span><a class="page-number" href="/page/3/">3</a><a class="page-number" href="/page/4/">4</a><span class="space">&hellip;</span><a class="page-number" href="/page/6/">6</a><a class="extend next" rel="next" href="/page/3/">下一页 &raquo;</a>
  </nav>

</section>
        
          <aside id="sidebar">
  
    
  <div class="widget-wrap">
    <h3 class="widget-title">最新文章</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/2021/01/08/前端鉴权总结/">前端鉴权总结</a>
          </li>
        
          <li>
            <a href="/2021/01/03/web性能优化/">web性能优化</a>
          </li>
        
          <li>
            <a href="/2020/12/31/nodejs入门/">nodejs 入门</a>
          </li>
        
          <li>
            <a href="/2020/12/27/vue单页面应用白屏屏优化/">vue单页面应用白屏屏优化</a>
          </li>
        
          <li>
            <a href="/2020/12/26/逸辰音乐问题点/">逸辰音乐问题点</a>
          </li>
        
      </ul>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">归档</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/01/">一月 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/12/">十二月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/09/">九月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/08/">八月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/07/">七月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/05/">五月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/04/">四月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/03/">三月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/02/">二月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/11/">十一月 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/10/">十月 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/07/">七月 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/06/">六月 2019</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">分类</h3>
    <div class="widget">
      <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/categories/ES6/">ES6</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/Vue/">Vue</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/css/">css</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/git/">git</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/html/">html</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/js/">js</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/nodejs/">nodejs</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/vue/">vue</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/前端/">前端</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/前端项目/">前端项目</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/服务端/">服务端</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/算法/">算法</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">标签</h3>
    <div class="widget">
      <ul class="tag-list"><li class="tag-list-item"><a class="tag-list-link" href="/tags/Apache/">Apache</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Array/">Array</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/BFC/">BFC</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/CORS/">CORS</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/DOM/">DOM</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/DOM-diff/">DOM diff</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/JSONP/">JSONP</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Vue3/">Vue3</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/absolute/">absolute</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ajax/">ajax</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/auto/">auto</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/background/">background</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/const/">const</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/devServer/">devServer</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/function/">function</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/git/">git</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/github/">github</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/hexo/">hexo</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/host/">host</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/html/">html</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/http/">http</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/jquery/">jquery</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/js/">js</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/leancloud/">leancloud</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/let/">let</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nodejs/">nodejs</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/npm/">npm</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/position/">position</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/promise/">promise</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/proxy/">proxy</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/script标签/">script标签</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/session-cookie/">session-cookie</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/token/">token</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/v-if/">v-if</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/v-show/">v-show</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vDOM/">vDOM</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vHost/">vHost</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vue/">vue</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vue-cli/">vue-cli</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vuec/">vuec</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vue生命周期/">vue生命周期</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/width/">width</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/z-index/">z-index</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/原型链/">原型链</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/同源/">同源</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/外边距/">外边距</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/层叠上下文/">层叠上下文</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/性能优化/">性能优化</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/数据结构/">数据结构</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/浏览器/">浏览器</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/百分比/">百分比</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/算法/">算法</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/精华/">精华</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/软件下载/">软件下载</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/鉴权/">鉴权</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">标签云</h3>
    <div class="widget tagcloud">
      <a href="/tags/Apache/" style="font-size: 10px;">Apache</a> <a href="/tags/Array/" style="font-size: 10px;">Array</a> <a href="/tags/BFC/" style="font-size: 10px;">BFC</a> <a href="/tags/CORS/" style="font-size: 10px;">CORS</a> <a href="/tags/DOM/" style="font-size: 10px;">DOM</a> <a href="/tags/DOM-diff/" style="font-size: 10px;">DOM diff</a> <a href="/tags/JSONP/" style="font-size: 10px;">JSONP</a> <a href="/tags/Vue3/" style="font-size: 10px;">Vue3</a> <a href="/tags/absolute/" style="font-size: 10px;">absolute</a> <a href="/tags/ajax/" style="font-size: 10px;">ajax</a> <a href="/tags/auto/" style="font-size: 10px;">auto</a> <a href="/tags/background/" style="font-size: 10px;">background</a> <a href="/tags/const/" style="font-size: 10px;">const</a> <a href="/tags/devServer/" style="font-size: 10px;">devServer</a> <a href="/tags/function/" style="font-size: 10px;">function</a> <a href="/tags/git/" style="font-size: 10px;">git</a> <a href="/tags/github/" style="font-size: 10px;">github</a> <a href="/tags/hexo/" style="font-size: 10px;">hexo</a> <a href="/tags/host/" style="font-size: 10px;">host</a> <a href="/tags/html/" style="font-size: 10px;">html</a> <a href="/tags/http/" style="font-size: 16.67px;">http</a> <a href="/tags/jquery/" style="font-size: 10px;">jquery</a> <a href="/tags/js/" style="font-size: 20px;">js</a> <a href="/tags/leancloud/" style="font-size: 10px;">leancloud</a> <a href="/tags/let/" style="font-size: 10px;">let</a> <a href="/tags/nodejs/" style="font-size: 10px;">nodejs</a> <a href="/tags/npm/" style="font-size: 10px;">npm</a> <a href="/tags/position/" style="font-size: 10px;">position</a> <a href="/tags/promise/" style="font-size: 10px;">promise</a> <a href="/tags/proxy/" style="font-size: 10px;">proxy</a> <a href="/tags/script标签/" style="font-size: 10px;">script标签</a> <a href="/tags/session-cookie/" style="font-size: 10px;">session-cookie</a> <a href="/tags/token/" style="font-size: 10px;">token</a> <a href="/tags/v-if/" style="font-size: 10px;">v-if</a> <a href="/tags/v-show/" style="font-size: 10px;">v-show</a> <a href="/tags/vDOM/" style="font-size: 10px;">vDOM</a> <a href="/tags/vHost/" style="font-size: 10px;">vHost</a> <a href="/tags/vue/" style="font-size: 13.33px;">vue</a> <a href="/tags/vue-cli/" style="font-size: 10px;">vue-cli</a> <a href="/tags/vuec/" style="font-size: 10px;">vuec</a> <a href="/tags/vue生命周期/" style="font-size: 10px;">vue生命周期</a> <a href="/tags/width/" style="font-size: 10px;">width</a> <a href="/tags/z-index/" style="font-size: 10px;">z-index</a> <a href="/tags/原型链/" style="font-size: 10px;">原型链</a> <a href="/tags/同源/" style="font-size: 10px;">同源</a> <a href="/tags/外边距/" style="font-size: 10px;">外边距</a> <a href="/tags/层叠上下文/" style="font-size: 10px;">层叠上下文</a> <a href="/tags/性能优化/" style="font-size: 13.33px;">性能优化</a> <a href="/tags/数据结构/" style="font-size: 10px;">数据结构</a> <a href="/tags/浏览器/" style="font-size: 16.67px;">浏览器</a> <a href="/tags/百分比/" style="font-size: 10px;">百分比</a> <a href="/tags/算法/" style="font-size: 10px;">算法</a> <a href="/tags/精华/" style="font-size: 10px;">精华</a> <a href="/tags/软件下载/" style="font-size: 10px;">软件下载</a> <a href="/tags/鉴权/" style="font-size: 10px;">鉴权</a>
    </div>
  </div>

  
</aside>
        
      </div>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      &copy; 2021 <a href="http://github.com/cai4633/cai4633.github.io" target="_blank">Cai4633</a><br>
      <!--Powered by <a href="http://hexo.io/" target="_blank">Hexo</a>-->
    </div>
  </div>
</footer>
    </div>
    <nav id="mobile-nav">
  
    <a href="/" class="mobile-nav-link">Home</a>
  
    <a href="/archives" class="mobile-nav-link">Archives</a>
  
</nav>
    

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/2.0.3/jquery.min.js"></script>


  <link rel="stylesheet" href="/fancybox/jquery.fancybox.css">
  <script src="/fancybox/jquery.fancybox.pack.js"></script>


<script src="/js/script.js"></script>



    <script>
      let isMobile = false
      if (/Android|webOS|iPhone|iPad|BlackBerry/i.test(navigator.userAgent)) {
        isMobile = true
        $("#nav").addClass("is-mobile")
        $("footer").addClass("is-mobile")
      }
      if ($(".local-search").size()) {
        $.getScript("/js/search.js", function () {
          searchFunc("/search.xml", "local-search-input", "local-search-result")
        })
      }
    </script>
  </div>
</body>
</html>